﻿using System;
using System.ServiceModel;
using System.Diagnostics;
using System.ComponentModel;
using BacteriumObserver;
using System.Collections.Generic;
using CommonDataContracts;

namespace Bacterium
{     
    
       
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class Bacterium : IBacterium, INotifyPropertyChanged
    {
        private const int DEFAULT_INITIAL_POWER = 10;
        private const int MAX_NUM_DOMAINS = 1;

        #region Fields
        private Guid _id;
        private int _teamID;
        private int _power;
        private BacteriumAction _currentAction;
        private Boolean _isAttacked;
        private Boolean _isSupported;
        private List<Uri> _domains;       
        #endregion

        #region Public Properties
        public Guid ID 
        {
            get
            {
                return _id;
            }

            private set 
            {
                _id = value;
                OnPropertyChanged("ID");
            } 
        }

        public int TeamID 
        {
            get
            {
                return _teamID;
            }

            private set
            {
                _teamID = value;
                OnPropertyChanged("TeamID");
            }
        }

        public int Power 
        {
            get
            {
                return _power;
            }

            private set
            {
                _power = value;
                OnPropertyChanged("Power");
            }
        }

        public BacteriumAction CurrentAction
        {
            get
            {
                return _currentAction;
            }

            private set
            {
                _currentAction = value;
                OnPropertyChanged("CurrentAction");
            }
        }

        public Boolean IsAttacked 
        {
            get
            {
                return _isAttacked;
            }

            private set 
            {
                _isAttacked = value;
                OnPropertyChanged("IsAttacked");
            }
        }

        public Boolean IsSupported 
        {
            get
            {
                return _isSupported;
            }

            private set 
            {
                _isSupported = value;
                OnPropertyChanged("IsSupported");
            }
        }

        public List<Uri> Domains
        {
            get { return _domains; }
            set { _domains = value; }
        } 

        //private Observer Observer { get;  set; }
        #endregion
        
        public event PropertyChangedEventHandler PropertyChanged;

        private Random _fairDice = new Random();     

        public Bacterium()
        {
            ID = System.Guid.NewGuid();
            TeamID = _fairDice.Next(8);
            Power = TeamID != 0 ? DEFAULT_INITIAL_POWER : 0;
            CurrentAction = BacteriumAction.IDLE;
            IsAttacked = false;
            IsSupported = false;
            Domains = GenerateRandomDomains(_fairDice.Next(1, MAX_NUM_DOMAINS));            

            
            DisplayProperties();
            BacteriumHelper.RegisterWithDomains(Domains, CreateSnapshot());
            PropertyChanged += new PropertyChangedEventHandler(UpdateSubscribers);
        }


        private void DisplayProperties()
        {
            Console.WriteLine("  Bacterium");
            foreach (var prop in typeof(Bacterium).GetProperties())
            {
                if (prop.PropertyType == typeof(List<Uri>))
                {
                    Console.Write("    {0}: ", prop.Name);
                    foreach (var uri in (List<Uri>)prop.GetValue(this, null))
                    {
                        Console.Write(" {0} ", uri);
                    }

                    Console.WriteLine();
                }
                else
                    Console.WriteLine("    {0}: {1} ", prop.Name, prop.GetValue(this, null));
            }
 
        }

        private List<Uri> GenerateRandomDomains(int n)
        {
            var domains = new List<Uri>();
            var prefix = "ldap:///bdid=";

            for (int i = 0; i < n; i++)
            {
                var domain = new Uri(prefix + _fairDice.Next(MAX_NUM_DOMAINS));
                
                if (!domains.Contains(domain))
                    domains.Add(domain);
            }

            return domains;
        }

        protected void OnPropertyChanged(String name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }

        #region ATTACK
        /// <summary>
        /// Attack request stub
        /// </summary>
        /// <param name="target"></param>
        private void Attack(IBacterium target)
        {
            if (CurrentAction != BacteriumAction.ATTACK)
	        {
                CurrentAction = BacteriumAction.ATTACK;
	        }

            target.TakeDamage();

            CurrentAction = BacteriumAction.IDLE;
            Console.WriteLine("  Attack message has been sent to target bacterium");
        }
    
        /// <summary>
        /// Attack response stub
        /// </summary>
        public void TakeDamage()
        {
            if (Power > 0)
            {
                if (!IsAttacked)
                {
                    IsAttacked = true;
                }

                if (--Power <= 0)
                {
                    TeamID = 0;
                    IsAttacked = false;
                    CurrentAction = BacteriumAction.IDLE;
                }
                
            }           
            
        }
        #endregion

        #region CAPTURE
        /// <summary>
        /// Capture request stub
        /// </summary>
        /// <param name="target">For now it's the explicit object - in WS it's gonna be a Proxy object initialized by IP, received from the observer via ID</param>
        private void Capture(IBacterium target)
        {
            if (CurrentAction != BacteriumAction.CAPTURE)
            {
                CurrentAction = BacteriumAction.CAPTURE;
            }

            target.ChangeTeam(TeamID);
            CurrentAction = BacteriumAction.IDLE;
            Console.WriteLine("  Capture message has been sent to target bacterium");
        }

        /// <summary>
        /// Capture response stub
        /// </summary>
        /// <param name="newTeamID"></param>
        public void ChangeTeam(int newTeamID)
        {
            if (TeamID == 0)
            {
                TeamID = newTeamID;
                //to avoid having zero power
                Power = _fairDice.Next(DEFAULT_INITIAL_POWER - 1) + 1;
                
            }
           
        }
        #endregion

        #region SUPPORT
        /// <summary>
        /// Support request stub
        /// </summary>
        /// <param name="target"></param>
        private void Support(IBacterium target)
        {
            //doesn't allow to heal hostile bacteria
            //if (TeamID == target.TeamID)
            //{
                if (CurrentAction != BacteriumAction.SUPPORT)
                {
                    CurrentAction = BacteriumAction.SUPPORT;
                }

                target.RestoreDamage();
                CurrentAction = BacteriumAction.IDLE;
                Console.WriteLine("  Support message has been sent to target bacterium");
            //}
           
        }

        /// <summary>
        /// Support response stub
        /// </summary>
        public void RestoreDamage()
        {
            
            if (Power < 100)
            {
                if (!IsSupported)
                {
                    IsSupported = true;
                }

                Power++;
                
            }
                
            
            

        }
        #endregion

        #region IDLE
        /// <summary>
        /// This is a local action, so no requests needed
        /// </summary>
        private void Idle()
        {
            if (Power < 100)
            {
                if (CurrentAction != BacteriumAction.IDLE)
                {
                    CurrentAction = BacteriumAction.IDLE;
                }
                Power++;
                
            }
        }
        #endregion

        private BacteriumSnapshot CreateSnapshot()
        {

            return new BacteriumSnapshot 
            {
                ID = this.ID.ToString(),
                TeamID = this.TeamID,
                Power = this.Power,
                CurrentAction = this.CurrentAction,
                IsAttacked= this.IsAttacked,
                IsSupported = this.IsSupported
            };
        }

        private void UpdateSubscribers(Object sender, PropertyChangedEventArgs e)
        {
            var prop = e.PropertyName;
            var newValue = typeof(Bacterium)
                .GetProperty(e.PropertyName)
                .GetValue(this, null);

            Console.WriteLine("  {0} = {1}", prop, newValue);

            BacteriumHelper.UpdateObservers(Domains, prop, newValue.ToString());
        }

        
        public void PerformAction()
        {
            //dead bacteria cannot act
            if (Power > 0 && TeamID != 0)
            {
                try
                {
                    BacteriumHelper.ReinitializeObserverEndpoint(Domains);

                    var target = BacteriumHelper.GetNeutralBacterium();

                    if (target != null)
                        Capture(target);
                        
                    else
                    {
                        if (BacteriumHelper.GetTeamBalance())
                        {
                            target = BacteriumHelper.GetWeakestEnemy();

                            if (target != null)
                                Attack(target);
                        }
                        else
                        {
                            if (BacteriumHelper.GetPowerRatio())
                            {
                                target = BacteriumHelper.GetWeakestEnemy();

                                if (target != null)
                                    Attack(target);
                            }
                            else
                            {
                                if (_fairDice.Next() > 0.5)
                                {
                                    target = BacteriumHelper.GetWeakestEnemy();

                                    if (target != null)
                                        Attack(target);
                                }
                                else
                                {
                                    if (Power == 100)
                                    {
                                        target = BacteriumHelper.GetWeakestAlly();

                                        if (target != null)
                                            Support(target);
                                    }
                                    Idle();
                                }
                            }
                        }
                    }
                }

                catch (Exception e)
                {
                    Console.WriteLine("ERROR: {0}",e.Message);
                } 
                

            }
           
            
        }

        
       
    }
}
